package main

import (
	"context"
	"encoding/json"
	"fmt"
	"lbs-sdk/app"
	"lbs-sdk/netfilter"
	"lbs-sdk/utils"
	"net"
	"os"
	"runtime/debug"
	"time"

	"gitlab.xunlei.cn/xacc/fastlogger"
	"gitlab.xunlei.cn/xacc/xlan"
	nd "gitlab.xunlei.cn/xacc/xlan/net_device"
)

type VpnConfig struct {
	Address      []xlan.Address
	FakeIp       string
	ProxyAll     bool
	LogMode      []string
	LogLevel     string
	Ipv4PassList []string
	FilterList   []string
	DumpPkg      bool
}

// 将字符串IP转换为net.IP
func stringToNetIP(ipStr string) net.IP {
	ip := net.ParseIP(ipStr)
	if ip == nil {
		// 解析失败，返回nil
		return nil
	}
	return ip
}

var vpnConfig VpnConfig
var configPath = "./vpn_config.json"

func OpenLogMode(cfg VpnConfig) {
	for _, mode := range cfg.LogMode {
		switch mode {
		case "filter":
			netfilter.OpenFilterLog = true
			fmt.Printf("open filter log\n")
		case "session":
			netfilter.OpenSessionLog = true
			fmt.Printf("open session log\n")
		}

	}
}

func InitLog(cfg VpnConfig) {
	switch cfg.LogLevel {
	case "trace":
		fastlogger.SetLevel(fastlogger.TraceLevel)
	case "debug":
		fastlogger.SetLevel(fastlogger.DebugLevel)
	case "info":
		fastlogger.SetLevel(fastlogger.InfoLevel)
	case "warn":
		fastlogger.SetLevel(fastlogger.WarnLevel)
	case "error":
		fastlogger.SetLevel(fastlogger.ErrorLevel)
	default:
		panic("invalid log level in vpn_config.json")
	}

	os.RemoveAll("nfapi.log")
	f, err := os.OpenFile("nfapi.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		panic("open log file failed: " + err.Error())
	}
	fastlogger.SetOutput(f)

	// log mode
	OpenLogMode(cfg)
}

// 安全地执行可能panic的函数
func safeExecute(name string, fn func() error) error {
	defer func() {
		if r := recover(); r != nil {
			fastlogger.Errorf("%s发生panic: %v\n堆栈信息:\n%s", name, r, debug.Stack())
			fmt.Printf("%s发生panic: %v\n", name, r)
		}
	}()

	return fn()
}

func Start() {
	if configPath != "" {
		cfg, err := os.ReadFile(configPath)
		if err != nil {
			panic(err.Error())
		}
		err = json.Unmarshal(cfg, &vpnConfig)
		if err != nil {
			panic(err.Error())
		}
	}
	if vpnConfig.FakeIp == "" {
		panic("fake ip is empty, please set it in vpn_config.json")
	} else {
		netfilter.FakeIp = stringToNetIP(vpnConfig.FakeIp)
		if netfilter.FakeIp == nil {
			panic("fake ip is invalid, please set a valid ip in vpn_config.json")
		}
	}
	netfilter.ProxyAll = vpnConfig.ProxyAll

	InitLog(vpnConfig)
	// utils.PkgDump = vpnConfig.DumpPkg
	fmt.Printf("loglevel : %v\n", vpnConfig.LogLevel)
	fmt.Printf("dumpPkg : %v\n", utils.PkgDump)
	var err error
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	// 设置用户虚拟ip

	// 连接vpn服务器
	link := nd.LinkInfo{
		UserId:          "123456",
		DstNodeId:       11,
		IngressProtocol: "kcp",
		IngressAddress:  "192.168.182.142:18082",
		InnerLocalIp:    "10.192.1.1",
	}
	err = app.ConnectWithXlanClient(ctx, &link)
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("xlan connected %s://%s\n", link.IngressProtocol, link.IngressAddress)

	// set fake ip
	netfilter.DefaultClientDriverNf.SetFakeIP(vpnConfig.FakeIp)

	// Init netfilter
	err = netfilter.Open()
	if err != nil {
		fastlogger.Errorf("nf_open : %s", err.Error())
		return
	}
	defer netfilter.Close()

	// 捕获应用程序发送出去的ip包
	for _, filter := range vpnConfig.FilterList {
		switch filter {
		case "ipv4pass":
			netfilter.DefaultFilterFnListCache.AddFilterFnList(netfilter.PassTargetIpv4(vpnConfig.Ipv4PassList))
		case "publicDstIp":
			netfilter.DefaultFilterFnListCache.AddFilterFnList(netfilter.PublicDstIp)
		default:
			panic(fmt.Sprintf("invalid filter name %s in vpn_config.json", filter))
		}
		fmt.Printf("add filter %s\n", filter)
	}
	select {}
}

func main() {
	// 添加panic捕获
	defer func() {
		if r := recover(); r != nil {
			fastlogger.Errorf("程序发生panic: %v", r)
			fmt.Printf("程序发生panic: %v\n", r)
			// 可以选择性地打印堆栈信息
			// debug.PrintStack()
			os.Exit(1)
		}
	}()
	go func() {
		for {
			fmt.Printf("ipv4Snmp:%v\n", netfilter.Ipv4Snmp)
			time.Sleep(time.Second)
		}
	}()
	Start()
}
